package base

import (
	"context"
	"errors"
	"fmt"
	"io"
	"net"
	"sync"
	"time"
	"zinx/interfa"
	"zinx/utils"
	"zinx/zlog"
)

type Connection struct {
	// 当前Conn属于哪个Server
	TcpServer interfa.IServer
	// 当前socket套接字(fd)
	Conn *net.TCPConn
	// 当前连接ID(sessionID)，全局唯一
	ConnID uint32
	// 该连接绑定的路由方法
	MsgHandler interfa.IMsgHandle
	// 告知该连接已经退出/停止的channel（协程程安全）
	ctx    context.Context
	cancel context.CancelFunc
	// 有缓冲管道，用于读写两个goroutine之间得消息通信
	msgBuffChan chan []byte
	sync.RWMutex
	// 连接属性
	property     map[string]interface{}
	propertyLock sync.RWMutex
	// 当前连接的关闭状态
	isClosed bool
}

func NewConntion(server interfa.IServer, conn *net.TCPConn, connID uint32, msgHandler interfa.IMsgHandle) *Connection {
	c := &Connection{
		TcpServer:   server,
		Conn:        conn,
		ConnID:      connID,
		isClosed:    false, // false未关闭
		MsgHandler:  msgHandler,
		msgBuffChan: make(chan []byte, utils.GlobalObject.MaxMsgChanLen),
		property:    make(map[string]interface{}),
	}
	c.TcpServer.GetConnMgr().Add(c)
	return c
}

func (c *Connection) StartReader() {
	defer func() {
		zlog.Debug(c.RemoteAddr().String(), " Conn Reader Exit !!!")
	}()

	// 读取数据(这里面可能有问题，但是目前读取粘包的数据正常)
	for {
		select {
		case <-c.ctx.Done():
			return
		default:
			// 读取客户端的Msg head
			headData := make([]byte, c.TcpServer.Packet().GetHeadLen())
			if _, err := io.ReadFull(c.Conn, headData); err != nil {
				zlog.Error(c.RemoteAddr().String(), "Read Msg Head Error: ", err)
				return
			}
			// 拆包，得到msgId和dataLen放在msg中
			msg, err := c.TcpServer.Packet().UnPack(headData)
			if err != nil {
				zlog.Error(c.RemoteAddr().String(), err)
				return
			}

			// 根据dataLen读取data，放到msg.Data中
			var data []byte
			if msg.GetDataLen() > 0 {
				data = make([]byte, msg.GetDataLen())
				if _, err := io.ReadFull(c.Conn, data); err != nil {
					zlog.Error(c.RemoteAddr().String(), "Read Msg Data Error: ", err)
					return
				}
			}
			msg.SetData(data)

			req := Request{
				conn: c,
				msg:  msg,
			}

			if utils.GlobalObject.WorkerPoolSize > 0 {
				// 已经启动工作池机制，将消息交给Worker处理
				c.MsgHandler.SendMsgToTaskQueue(&req)
			} else {
				// 若没有工作机制，直接执行消息回调
				go c.MsgHandler.DoMsgHandler(&req)
			}
		}
	}
}

func (c *Connection) StartWriter() {
	defer func() {
		zlog.Debug(c.RemoteAddr().String(), " Conn Writer Exit !!!")
	}()

	for {
		select {
		case data, ok := <-c.msgBuffChan:
			if ok {
				if _, err := c.Conn.Write(data); err != nil {
					zlog.Error(c.RemoteAddr().String(), "Send Buff Data Error:, ", err)
					return
				}
				if msg, err := c.TcpServer.Packet().UnPack(data); err == nil {
					zlog.Debug("Send msg: ", msg)
				}
			} else {
				zlog.Debug(c.RemoteAddr().String(), "MsgBuffChan Is Closed")
			}
		case <-c.ctx.Done():
			// conn已关闭
			return
		}
	}
}

// 启动该连接，让当前连接开始工作
func (c *Connection) Start() {
	// 按照用户传递进来的创建连接时需要处理的业务，执行钩子方法
	c.TcpServer.CallOnConnStart(c)
	c.ctx, c.cancel = context.WithCancel(context.Background())
	// 用户从客户端读取数据到Goroutine
	go c.StartReader()

	// 用户往客户端写数据得Goroutine
	go c.StartWriter()

	select {
	case <-c.ctx.Done():
		c.finalizer()
		return
	}
}

func (c *Connection) Stop() {
	// 关闭，发送信号
	c.cancel()
}

func (c *Connection) finalizer() {
	c.TcpServer.CallOnConnStop(c)

	c.Lock()
	defer c.Unlock()

	//如果当前链接已经关闭
	if c.isClosed == true {
		return
	}

	// 关闭socket链接
	_ = c.Conn.Close()

	//将链接从连接管理器中删除
	c.TcpServer.GetConnMgr().Remove(c)

	//关闭该链接全部管道
	close(c.msgBuffChan)
	//设置标志位
	c.isClosed = true
}

// 从当前连接获取原始的socket TCPConn
func (c *Connection) GetTCPConnection() *net.TCPConn {
	return c.Conn
}

// 获取当前连接ID
func (c *Connection) GetConnID() uint32 {
	return c.ConnID
}

// 获取远程客户端地址信息
func (c *Connection) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

// 直接将Message数据发送数据给远程的TCP客户端（无缓冲)
func (c *Connection) SendMsg(msgId uint32, data []byte) error {
	c.RLock()
	defer c.RUnlock()
	if c.isClosed == true {
		return errors.New("Connection Closed When Send Msg")
	}

	// 封包，发送
	dp := c.TcpServer.Packet()
	msg, err := dp.Pack(NewMsgPackage(msgId, data))
	if err != nil {
		return errors.New(fmt.Sprint("Pack Error Msg Id: ", msgId))
	}

	_, err = c.Conn.Write(msg)

	return err
}

// 直接将Message发送远程TCP客户端（有缓冲)
func (c *Connection) SendBuffMsg(msgId uint32, data []byte) error {
	c.RLock()
	defer c.RUnlock()

	idleTimeout := time.NewTimer(5 * time.Millisecond)
	defer idleTimeout.Stop()

	if c.isClosed == true {
		return errors.New("Connection Closed When Send Buff Msg")
	}
	// 封包，发送
	dp := c.TcpServer.Packet()
	msg, err := dp.Pack(NewMsgPackage(msgId, data))
	if err != nil {
		return errors.New(fmt.Sprint("Pack Error Msg Id: ", msgId))
	}

	// 超时判断(网络堵塞情况下，需要加个超时判断，否则会堵塞服务，网络恢复后，服务继续运行)
	select {
	case <-idleTimeout.C:
		return errors.New("Send buff msg timeout")
	case c.msgBuffChan <- msg:
		return nil
	}
}

// 设置连接属性
func (c *Connection) SetProperty(key string, value interface{}) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()
	c.property[key] = value
}

// 获取连接属性
func (c *Connection) GetProperty(key string) (interface{}, error) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()

	if value, ok := c.property[key]; ok {
		return value, nil
	}
	return nil, errors.New("no property found")
}

// 移除连接属性
func (c *Connection) RemoveProperty(key string) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()
	delete(c.property, key)
}

// 饭绘ctx，用于用户自定义go程序获取链接退出状态
func (c *Connection) Context() context.Context {
	return c.ctx
}
